#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>

#define CAPACITY 1024
#define OPTION_NUM 20
#define trimSpace(ptr) do{\
            while (isspace(*ptr)) ++ptr;\
        }while (0)

// 重定向选项
enum dir_Option
{
    None_redir = 0,
    Input_redir,
    Output_redir,
    Append_redir
};

char command[CAPACITY];
char *myargv[OPTION_NUM];
// 重定向类型
enum dir_Option redirType = None_redir;
// 重定向文件描述符
size_t fd = 0;
// 重定向文件名
char *redir_filename = NULL;

void get_command()
{
    printf("用户名@主机名 当前路径# ");
    fflush(stdout);
    // 输入命令
    char *s = fgets(command, sizeof(command) - 1, stdin); // 读取1023个字符,最后一个字符预留/0
    assert(s);
    (void)s;
    command[strlen(command) - 1] = 0; // 处理换行符
#ifdef DEBUG_FIRST
    printf(s);
#endif
}

void redir_check()
{
    assert(command);
    char *ptr = command;
    while (*ptr)
    {
        if (*ptr == '>')
        {
            redirType = Output_redir;
            *ptr++ = '\0';
            if (*ptr == '>')
            {
                redirType = Append_redir;
                *ptr++ = ' ';
            }
            trimSpace(ptr);
            redir_filename = ptr;
            break;
        }
        else if (*ptr == '<')
        {
            redirType = Input_redir;
            *ptr++ = '\0';
            trimSpace(ptr);
            redir_filename = ptr;
            break;
        }
        else
        {
            ptr++;
        }
    }
}

void tok_command()
{
    assert(command);
    size_t i = 0;
    myargv[i++] = strtok(command, " ");
    if (myargv[0] != NULL && (strcmp(myargv[0], "ls") == 0))
        myargv[i++] = "--color=auto";
    while (myargv[i++] = strtok(NULL, " "));
#ifdef DEBUG_SECOND
    for (size_t index = 0; myargv[index]; index++)
        printf("%s ", myargv[index]);
#endif
}

void Operator_Redir()
{
    if (redir_filename)
    {
        int redirOption = 0;
        switch (redirType)
        {
        case None_redir:
            break;
        case Input_redir:
        {
            redirOption |= O_RDONLY;
            fd = open(redir_filename, redirOption);
            dup2(fd, 0);
            break;
        }
        case Output_redir:
        case Append_redir:
        {
            redirOption = O_WRONLY | O_CREAT | O_TRUNC;
            if(redirType == Append_redir)
                redirOption = O_WRONLY | O_CREAT | O_APPEND;
            fd = open(redir_filename, redirOption,0666);
            dup2(fd, 1);
            break;
        }
        }
    }
}

int main()
{
    int last_RetID = 0;
    while (1)
    {
        umask(0);
        // 获取命令
        get_command();
        // 重定向检查
        redir_check();
        // 提取命令参数
        tok_command();
        //检测内建命令
        if(myargv[0] != NULL && (strcmp(myargv[0], "cd") == 0))
        {
            if(myargv[1] != NULL)
                chdir(myargv[1]);
            continue;
        }
        else if(myargv[0] != NULL && (strcmp(myargv[0],"echo") == 0) && (strcmp(myargv[1],"$?") == 0))
        {
            printf("%d\n",last_RetID);
            continue;
        }
        // 创建子进程
        pid_t id = fork();
        assert(id != -1);
        if (id == 0)
        {
            //重定向操作
            Operator_Redir();
            execvp(myargv[0], myargv);
            perror("execvp:");
            exit(1);
        }
        close(id);
        int state = 0;
        pid_t ret = waitpid(id, &state, 0);
        assert(ret == id);
        (void)ret;
        fd = 0;
        redir_filename = NULL;
        redirType = None_redir;
        last_RetID = (state >> 8);
    }
    return 0;
}
